/*
 * Copyright 2007 The semap project development team.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Created by myn at 07.08.2007 15:49:41.
 *
 * Implementation of the Contact Service.
 *
 * $Id: ContactServiceImpl.java 438 2007-08-29 11:17:36Z Mike.Navern $
 */
package com.google.code.contactserv.service;

import com.google.code.contactserv.ContactServException;
import com.google.code.contactserv.common.makers.SPARQLMaker;
import com.google.code.contactserv.model.Contact;
import com.google.code.contactserv.model.PropertyHolder;
import com.hp.hpl.jena.query.*;
import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.util.FileManager;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;


/**
 * @author myn
 */
public class ContactServiceImpl implements ContactService {

    private static ContactServiceImpl instance = new ContactServiceImpl();

    private String inputFileName = "contactserv.rdf";
    private Model  model = ModelFactory.createDefaultModel();

    private ContactServiceImpl() {
    }

    public Model getModel() {
        return model;
    }

    /**
     * FIXME Replace the Singleton worst pattern by using an IoC container (Spring or PicoContainer)
     *
     * @return Unique instance of the class
     */
    public static ContactService getInstance() {
        return instance;
    }

    /**
     * Creates new Contact object
     */
    public Contact createContact(String uri) {
    	return new Contact(model, uri);
    }

    /* (non-Javadoc)
     * @see contactserv.ContactService#add()
     */
    public void add(Contact contact) {
        // TODO Auto-generated method stub
    }

    /** (non-Javadoc)
     *  @see com.google.code.contactserv.service.ContactService#find()
     */
    public List<Contact> find() {
        List<PropertyHolder> props = new ArrayList<PropertyHolder>();
        return find(props);
    }

    /** (non-Javadoc)
     *  @see com.google.code.contactserv.service.ContactService#find()
     */
    public List<Contact> find(List<PropertyHolder> props) {
        List<Contact> result = new ArrayList<Contact>();
        // Create a new query
//        StringBuffer queryString = new StringBuffer();

        Query query = QueryFactory.create(SPARQLMaker.makeSPARQLReq(props));

        // Execute the query and obtain results
        QueryExecution queryExecution = QueryExecutionFactory.create(query, model);
        ResultSet results = queryExecution.execSelect();

        ResultSet res = results;
        while(res.hasNext()) {
            QuerySolution qs = res.nextSolution();
            Resource      rs = qs.getResource("person");
            Contact       contact = createContact(rs.getURI());
            result.add(contact);
        }
        
        // Important - free up resources used running the query
        queryExecution.close();

        return result;
    }

//    public List<Contact> findAll() {
//        // FIXME *** ContactServiceImplTest.testFindAll() don't pass! ***
//        List<Contact> result = new ArrayList<Contact>();
//
//        Resource foafRes = model.getResource((FOAF.Person).toString());
//        ResIterator contacts = model.listSubjectsWithProperty(RDF.type, foafRes);
//
//        while (contacts.hasNext()) {
//            Resource person = contacts.nextResource();
//            Contact contact = new Contact();
//            contact.setUri(person.getURI());
//            contact.setResource(person);
//
//            result.add(contact);
//        }
//
//        return result;
//    }

//    public List<Contact> findByNick(String nickName) {
//        List<Contact> result = new ArrayList<Contact>();
//
//        Property foafNickRes = model.getProperty((FOAF.nick).toString());
//        ResIterator contacts = model.listSubjectsWithProperty(foafNickRes);
//
//        Property nick = model.getProperty((FOAF.nick).toString());
//        while (contacts.hasNext()) {
//            Resource person = contacts.nextResource();
//            Statement statement = person.getProperty(nick);
//            if (statement != null && statement.getString().equals(nickName)) {
//                Contact contact = new Contact();
//                contact.setUri(person.getURI());
//                contact.setResource(person);
//
//                result.add(contact);
//            }
//        }
//        return result;
//    }

    public List<Contact> findSPARQL(String queryString) {
        // FIXME *** ContactServiceImplTest.testFindByGender() don't pass! ***
        List<Contact> result = new ArrayList<Contact>();

        Query query = QueryFactory.create(queryString);

        // Execute the query and obtain results
        QueryExecution queryExecution = QueryExecutionFactory.create(query, model);
        ResultSet ress = queryExecution.execSelect();

        while (ress.hasNext()) {
            Contact contact = new Contact();
            QuerySolution soln = ress.nextSolution();
//            RDFNode x = soln.get("varName");       // Get a result variable by name.
            Resource r = soln.getResource("person"); // Get a result variable - must be a resource
//            Literal l = soln.getLiteral("resource");   // Get a result variable - must be a litera
            contact.setUri(r.getURI());
            contact.setResource(r);

            result.add(contact);
        }
        
        // Important - free up resources used running the query
        queryExecution.close();

        return result;
    }

    /* (non-Javadoc)
     * @see contactserv.ContactService#exist()
     */
    public boolean exist(String uri) {
        Resource     res  = model.getResource(uri);
        StmtIterator iter = model.listStatements(res, (Property)null, 
                (RDFNode)null);
        boolean      rc   = false;
        try {
            if (iter.hasNext()) {
                //Statement stmt = iter.nextStatement();
                //System.out.print(stmt.toString());
                rc = true;
            }
        } catch (Exception e) {
            // TODO: handle exception
        }
        return rc;
    }

    /* (non-Javadoc)
     * @see contactserv.ContactService#get()
     */
    public Contact get(String uri) {
        return createContact(uri);
    }

    /* (non-Javadoc)
     * @see contactserv.ContactService#init(java.lang.String)
     */
    public void init(String fileName) throws ContactServException {
        inputFileName = fileName;
        loadRDF();
    }

    /* (non-Javadoc)
     * @see contactserv.ContactService#remove()
     */
    public void remove(String uri) {
        Resource     res  = model.getResource(uri);
        StmtIterator iter = model.listStatements(res, (Property)null, 
                (RDFNode)null);

        try {
            if (iter.hasNext()) {
                Statement stmt = iter.nextStatement();
                stmt.remove();
            }
        } catch (Exception e) {
            // TODO: handle exception
        }
    }

    /* (non-Javadoc)
     * @see contactserv.ContactService#update()
     */
    public void update() {
        // TODO Auto-generated method stub
    }

    /**
     * Load contacts from repository.
     *
     * @throws ContactServException *
     */
    private void loadRDF() throws ContactServException {
        InputStream in;

        in = FileManager.get().open(inputFileName);
        if (in == null) {
            throw new ContactServException(
                    ContactServException.RDF_FILE_DOESNOT_EXIST,
                    "File: " + inputFileName + "not found");
        }

        // read the RDF/XML file
        model.read(in, "");
    }
}
